/**
 * @file vdev.cpp
 * @brief vdev class
 * @author marco
 * @version v1.0
 * @date 2012-08-21
 */
#include "vdev.h"

vdev::vdev() {
	this->fd = -1;
	this->n_buffers = -1;
	this->buffers = NULL;
	CLEAR(cap);
	CLEAR(fmtdesc);
	CLEAR(format);
	CLEAR(cropcap);
	CLEAR(crop);
	CLEAR(req);
}

vdev::~vdev(){
	close_dev();
}

int vdev::get_system_video_dev_num(){
	return 0;
}

char* vdev::get_dev_path_by_id(const int id){
	return NULL;
}

/**
 * @brief opendev
 *
 * @param path video device file path.Ex:"/dev/video0"
 * @return file handle
 */
int vdev::open_dev(const char* path){
	if (NULL == path) {
		printf("Dev path error...\n");
		return -1;
	}
	if (fd > 0) {
		printf("Dev was opened...\n");
		return -2;
	}
	fd = open(path, O_RDWR | O_EXCL);
	if (fd <= 0) {
		printf("Dev open error...\n");
		return -3;
	}
	D(DEBUG_INF,"fd [%d]", fd);
	return fd;
}

/**
 * @brief close opened device
 */
void vdev::close_dev() {
	if (fd > 0) {
		close(fd);
	}
	fd = 0;
}

/**
 * @brief get video device capability;
 *
 * @return On success zero is returned.
 */
int vdev::get_capability(){
	int ret = -1;
	if (0 >= fd) {
		return ret;
	}
	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
	D(DEBUG_WAR, "IOCTL RETURN [%d]", ret);
	if (0 == ret) {
		D(DEBUG_INF, "Device Name [%s]", cap.driver);
		D(DEBUG_INF, "Device Card [%s]", cap.card);
		D(DEBUG_INF, "Device bus_info [%s]", cap.bus_info);
		D(DEBUG_INF, "Device version [%d]", cap.version);
		D(DEBUG_INF, "Device capabilities [%x]", cap.capabilities);
	}
	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) {
		D(DEBUG_WAR, "Device [%s] is no video capture or output device.", cap.driver);
		ret = -1;
	}
	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
		D(DEBUG_WAR, "Device [%s] does not support streaming i/o.", cap.driver);
		ret = -1;
	}
	return ret;
}

/**
 * @brief get device support output video format
 *
 * @return On success zero is returned.
 */
int vdev::get_fmtdesc(){
	int ret = -1;
	if (0 >= fd) {
		return ret;
	}
	fmtdesc.index = 0;
	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	while (-1 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) {
		D(DEBUG_INF, "Device index [%d]", fmtdesc.index);
		D(DEBUG_INF, "Device type [%d]", fmtdesc.type);
		D(DEBUG_INF, "Device description [%s]", fmtdesc.description);
		D(DEBUG_INF, "Device pixelformat [%x]", fmtdesc.pixelformat);
		int a, b, c, d;
		a = fmtdesc.pixelformat & 0xFF;
		b = (fmtdesc.pixelformat >> 8) & 0xFF;
		c = (fmtdesc.pixelformat >> 16) & 0xFF;
		d = (fmtdesc.pixelformat >> 24) & 0xFF;
		D(DEBUG_INF, "Device pixelformat [%c][%c][%c][%c]", a, b, c, d);

		++fmtdesc.index;
		ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	}
	return ret;
}

/**
 * @brief 
 *
 * @return 
 */
int vdev::get_cropcap() {
	int ret = -1;
	if (0 >= fd) {
		return ret;
	}
	CLEAR(cropcap);
	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
    	cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	}else if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) {
    	cropcap.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
	}
    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_CROPCAP, &cropcap);
	D(DEBUG_INF, "VIDIOC_CROPCAP return [%d] ", ret);
	if (0 == ret) {
		D(DEBUG_INF, "VIDIOC_CROPCAP crop.type [%d]", cropcap.type);
		D(DEBUG_INF, "VIDIOC_CROPCAP crop.type [%d][%d][%d][%d]", 
				cropcap.defrect.left, 
				cropcap.defrect.top, 
				cropcap.defrect.width, 
				cropcap.defrect.height
				);
    }
	return ret;
}

/**
 * @brief get video output format 
 *
 * @param w video output width size.
 * @param h video output height size.
 *
 * @return On success zero is returned.
 */
int vdev::set_format(const int w, const int h) {
	int ret = -1;
	if (0 >= fd) {
		return ret;
	}
    CLEAR(format);

    format.type = cropcap.type;
	D(DEBUG_INF, " Format type [%d] ", format.type);
	if (0 >= w || 0 >= h) {
		format.fmt.pix.width = cropcap.defrect.width;
		format.fmt.pix.height = cropcap.defrect.height;
	}else{
		format.fmt.pix.width = w;
		format.fmt.pix.height = h;
	}
	format.fmt.pix.pixelformat = fmtdesc.pixelformat;
    format.fmt.pix.field = V4L2_FIELD_INTERLACED;

	ret = ioctl(fd, VIDIOC_S_FMT, &format);
	D(DEBUG_WAR, "VIDIOC_S_FMT return [%d]", ret);
	if (0 != ret) {
		D(DEBUG_WAR, "VIDIOC_S_FMT error [%d]", errno);
	}
	return ret;
}

/**
 * @brief Get video device std mode.
 *
 * @return On success zero is returned.
 */
int vdev::get_std() {
	int ret = -1;
	if (0 >= fd) {
		return ret;
	}
    CLEAR(std);
	do {  
		ret = ioctl(fd, VIDIOC_QUERYSTD, &std);  
	} while (ret == -1 && errno == EAGAIN);  
	D(DEBUG_INF, "VIDIOC_QUERYSTD std [%ld]", std);
	return ret;
}

/**
 * @brief Get current device name.
 *
 * Get device name need call get_device_capability() first.
 *
 * @return On success zero is returned.
 */
unsigned char* vdev::get_device_name() {
	return cap.driver;
}

int vdev::init_mmap()
{
	CLEAR(req);

	req.count = BUFF_COUNTS;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;

	if(-1 == ioctl(fd, VIDIOC_REQBUFS, &req)) {
		if(EINVAL == errno) {
			D(DEBUG_ERR, "VIDIOC_REQBUFS error [%d]", errno);
			return -1;
		} else {
			D(DEBUG_ERR, "VIDIOC_REQBUFS error [%d]", errno);
			return -1;
		}
	}


	D(DEBUG_INF, "VIDIOC_REQBUFS buff counts [%d]", req.count);
	if(req.count < 2) {
		D(DEBUG_INF, "VIDIOC_REQBUFS error [%d] [%d]", errno, req.count);
		return -1;
	}

	buffers = (buffer*)calloc(req.count, sizeof(*buffers));

	if(!buffers) {
		D(DEBUG_INF, "calloc error. [%d]", errno);
		return -1;
	}

	for(n_buffers = 0; n_buffers < req.count; ++n_buffers) {
		v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		buf.index = n_buffers;

		if(-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf)) {
			D(DEBUG_ERR, "VIDIOC_QUERYBUF [%d]", errno);
			return -1;
		}

		buffers[n_buffers].length = buf.length;
		buffers[n_buffers].start =
			mmap(NULL, // start anywhere
					buf.length,
					PROT_READ | PROT_WRITE,
					MAP_SHARED,
					fd, buf.m.offset);

		if(MAP_FAILED == buffers[n_buffers].start) {
			D(DEBUG_ERR, "mmap [%d]", errno);
			return -1;
		}
	}
	return 0;

}

void vdev::uninit_mmap() {
    unsigned int i;
    for(i = 0; i < n_buffers; ++i) {
        if(-1 == munmap(buffers[i].start, buffers[i].length)) {
			D(DEBUG_ERR, "munmmap [%d]", errno);
        }

    }
    free(buffers);
}

